﻿/*
* Copyright (c) 2008 michiyasu wada
* http://www.seyself.com/
* 
* Distributed under The MIT License.
* [http://www.opensource.org/licenses/mit-license.php]
*/

//****************************************************************************
// ActionScript Standard Library
// flash.geom.Matrix object
//****************************************************************************

import flash7.geom.Point;

class flash7.geom.Matrix
{
    public var a:Number  = 1;
    public var b:Number  = 0;
    public var c:Number  = 0;
    public var d:Number  = 1;
    public var tx:Number = 0;
    public var ty:Number = 0;
    
    private var u:Number = 0;
    private var v:Number = 0;
    private var w:Number = 1;
    
    
    public function Matrix(a:Number, b:Number, c:Number, d:Number, tx:Number, ty:Number)
    {
        this.a  = ( isNaN(a ) ) ? 1 : a;
        this.b  = ( isNaN(b ) ) ? 0 : b;
        this.c  = ( isNaN(c ) ) ? 0 : c;
        this.d  = ( isNaN(d ) ) ? 1 : d;
        this.tx = ( isNaN(tx) ) ? 0 : tx;
        this.ty = ( isNaN(ty) ) ? 0 : ty;
        this.u = 0;
        this.v = 0;
        this.w = 1;
    }
    
    public function clone():Matrix
    {
        return new Matrix( a, b, c, d, tx, ty );
    }
    
    public function concat( m:Matrix ):Void
    {
        var _a:Number  = a  * m.a  + b  * m.c  + tx * m.u;
        var _b:Number  = a  * m.b  + b  * m.d  + tx * m.v;
        var _tx:Number = tx * m.a  + ty * m.c  + w  * m.tx;
        
        var _c:Number  = c  * m.a  + d  * m.c  + ty * m.u;
        var _d:Number  = c  * m.b  + d  * m.d  + ty * m.v;
        var _ty:Number = tx * m.b  + ty * m.d  + w  * m.ty;
        
        //var _u:Number  = u  * m.a  + v  * m.c  + w  * m.u;
        //var _v:Number  = u  * m.b  + v  * m.d  + w  * m.v;
        //var _w:Number  = u  * m.tx + v  * m.ty + w  * m.w;
        
        a = _a;   b = _b;   tx = _tx;
        c = _c;   d = _d;   ty = _ty;
        //u = _u;   v = _v;   w  = _w;
    }
        

    public function invert():Void
    {
        var det:Number = a*(d*w-ty*v) + c*(v*tx-w*b) + u*(b*ty-tx*d);
        if ( det == 0 ) det = 1;
        
        var x:Number = tx;
        var y:Number = ty;
        var _a  = ( d * w - y * v ) / det;
        var _b  = ( x * v - b * w ) / det;
        var _tx = ( y * b - x * d ) / det;
        var _c  = ( y * u - c * w ) / det;
        var _d  = ( a * w - x * u ) / det;
        var _ty = ( x * c - y * a ) / det;
        //var _u  = ( c * v - d * u ) / det;
        //var _v  = ( b * u - a * v ) / det;
        //var _w  = ( a * d - b * c ) / det;
        
        a = _a;   b = _b;//   tx = _tx;
        c = _c;   d = _d;//   ty = _ty;
        //u = _u;   v = _v;   w  = _w;
        
        tx = -x * a + -y * c;
        ty = -x * b + -y * d;
    }
    
    public function identity():Void
    {
        a = 1;  b = 0;  tx = 0;
        c = 0;  d = 1;  ty = 0;
        u = 0;  v = 0;  w  = 1;
    }
    
    public function createBox( scaleX:Number, scaleY:Number, rotation:Number, tx:Number, ty:Number ):Void
    {
        identity();
        _trans( scaleX, scaleY, rotation, tx, ty );
    }
    
    public function createGradientBox ( width:Number, height:Number, rotation:Number, tx:Number, ty:Number ):Void
    {
        identity();
        var t:Number = 0.0006103515625;
        var angle:Number = rotation;
        var nx:Number = t * width;
        var ny:Number = t * height;
        var x:Number = width  / 2 + tx;
        var y:Number = height / 2 + ty;
        _trans( nx, ny, angle, x, y );
    }
    
    public function rotate( angle:Number ):Void
    {
        _trans( 1, 1, angle, 0, 0 );
    }
    
    public function translate( tx:Number, ty:Number ):Void
    {
        _trans( 1, 1, 0, tx, ty );
    }
    
    public function scale( sx:Number, sy:Number ):Void
    {
        _trans( sx, sy, 0, 0, 0 );
    }
    
    public function deltaTransformPoint( pt:Point ):Point
    {
        return _transPoint( pt, 0, 0 );
    }
    
    public function transformPoint( pt:Point ):Point
    {
        return _transPoint( pt, tx, ty );
    }
    
    public function toString():String
    {
        return "(a="+ a +", b="+ b +", c="+ c +", d="+ d +", tx="+ tx +", ty="+ ty +")";
    }
    
    // private methods ================================================
    
    private function _trans( scaleX:Number, scaleY:Number, rotation:Number, _tx:Number, _ty:Number ):Void
    {
        var ts:Number = a * d - b * c;
        var sx:Number = Math.sqrt(a * a + c * c);
        var sy:Number = ts / sx;
        var angle:Number = Math.acos( a / sx ) + rotation;
        var nx:Number = scaleX * sx;
        var ny:Number = scaleY * sy;
        var cosa:Number = Math.cos( angle );
        var sina:Number = Math.sin( angle );
        
        a =  cosa * nx;
        b =  sina * ny;
        c = -sina * nx;
        d =  cosa * ny;
        tx += _tx;
        ty += _ty;
    }
    
    private function _transPoint( pt:Point, x:Number, y:Number ):Point
    {
        var ts:Number = a * d - b * c;
        var sx:Number = Math.sqrt(a * a + c * c);
        var sy:Number = ts / sx;
        var angle:Number = Math.acos( a / sx );
        
        var dx:Number = pt.x * sx;
        var dy:Number = pt.y * sy;
        var cosa = Math.cos( angle );
        var sina = Math.sin( angle );
        var nx:Number = cosa * dx - sina * dx;
        var ny:Number = sina * dy + cosa * dy;
        return new Point( nx + x , ny + y );
    }
    
    
}